﻿using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;

namespace ELab.Controls.flatstyle
{
    public class FlatDateTimePicker : DateTimePicker
    {
        #region ComboInfoHelper
        internal class ComboInfoHelper
        {
            [DllImport("user32")]
            private static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref ComboBoxInfo info);

            #region RECT struct
            [StructLayout(LayoutKind.Sequential)]
            private struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }
            #endregion

            #region ComboBoxInfo Struct
            [StructLayout(LayoutKind.Sequential)]
            private struct ComboBoxInfo
            {
                public int cbSize;
                public RECT rcItem;
                public RECT rcButton;
                public IntPtr stateButton;
                public IntPtr hwndCombo;
                public IntPtr hwndEdit;
                public IntPtr hwndList;
            }
            #endregion

            public static int GetComboDropDownWidth()
            {
                ComboBox cb = new ComboBox();
                int width = GetComboDropDownWidth(cb.Handle);
                cb.Dispose();
                return width;
            }
            public static int GetComboDropDownWidth(IntPtr handle)
            {
                ComboBoxInfo cbi = new ComboBoxInfo();
                cbi.cbSize = Marshal.SizeOf(cbi);
                GetComboBoxInfo(handle, ref cbi);
                int width = cbi.rcButton.Right - cbi.rcButton.Left;
                return width;
            }
        }
        #endregion

        [DllImport("user32.dll", EntryPoint = "SendMessageA")]
        private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, object lParam);
        [DllImport("user32")]
        private static extern IntPtr GetWindowDC(IntPtr hWnd);
        [DllImport("user32")]
        private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        const int WM_ERASEBKGND = 0x0014;
        const int WM_PAINT = 0x000F;
        const int WM_SETCURSOR = 0x0020;

        const int WM_NC_HITTEST = 0x0084;
        const int WM_NC_PAINT = 0x0085;

        const int WM_PRINTCLIENT = 0x0318;

        private Color _BorderColor = SystemColors.ControlDark;
        private Pen BorderPen = new Pen(SystemColors.ControlDark, 0);
        private Pen BorderPenControl = new Pen(SystemColors.ControlDark, 1);
        private bool DroppedDown = false;
        private int InvalidateSince = 0;
        private static int DropDownButtonWidth = 15;

        static FlatDateTimePicker()
        {
            // 2 pixel extra is for the 3D border around the pulldown button on the left and right
            //DropDownButtonWidth = ComboInfoHelper.GetComboDropDownWidth() + 2;	
            DropDownButtonWidth = ComboInfoHelper.GetComboDropDownWidth();
        }

        public FlatDateTimePicker()
            : base()
        {
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        }

        public Color BorderColor
        {
            get { return _BorderColor; }
            set
            {
                _BorderColor = value;
                BorderPen.Color = _BorderColor;
                BorderPenControl.Color = _BorderColor;
            }
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            base.OnValueChanged(eventargs);
            this.Invalidate();
        }

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            IntPtr hDC = IntPtr.Zero;
            Graphics gdc = null;
            switch (m.Msg)
            {
                case WM_NC_PAINT:
                    hDC = GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);
                    SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
                    SendPrintClientMsg();
                    SendMessage(this.Handle, WM_PAINT, IntPtr.Zero, 0);
                    OverrideDropDown(gdc);
                    OverrideControlBorder(gdc);
                    m.Result = (IntPtr)1;	// indicate msg has been processed
                    ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;
                case WM_PAINT:
                    base.WndProc(ref m);
                    hDC = GetWindowDC(m.HWnd);
                    gdc = Graphics.FromHdc(hDC);
                    OverrideDropDown(gdc);
                    OverrideControlBorder(gdc);
                    ReleaseDC(m.HWnd, hDC);
                    gdc.Dispose();
                    break;

                // We don't need this anymore, handle by WM_SETCURSOR
                case WM_NC_HITTEST:
                    base.WndProc(ref m);
                    if (DroppedDown)
                    {
                        OverrideDropDown(gdc);
                        OverrideControlBorder(gdc);
                    }
                    break;
                case WM_SETCURSOR:
                    base.WndProc(ref m);
                    // The value 3 is discovered by trial on error, and cover all kinds of scenarios
                    // InvalidateSince < 2 wil have problem if the control is not in focus and dropdown is clicked
                    if (DroppedDown)
                    {
                        Invalidate();
                        InvalidateSince++;
                    }
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        private void SendPrintClientMsg()
        {
            // We send this message for the control to redraw the client area
            Graphics clientGraphics = this.CreateGraphics();
            IntPtr intPtrClientDC = clientGraphics.GetHdc();
            SendMessage(this.Handle, WM_PRINTCLIENT, intPtrClientDC, 0);
            clientGraphics.ReleaseHdc(intPtrClientDC);
            clientGraphics.Dispose();
        }

        private void OverrideDropDown(Graphics g)
        {
            if (g == null) return;
            if (System.Environment.OSVersion.Version.Major >= 6)
            {
                g.DrawLine(BorderPenControl, this.Width - 1, 0, this.Width - 1, this.Height);
                return;
            }
            if (!this.ShowUpDown)
            {
                Rectangle rect = new Rectangle(this.Width - DropDownButtonWidth - 1, 0, DropDownButtonWidth + 1, this.Height);
                ControlPaint.DrawComboButton(g, rect, ButtonState.Flat);
                g.DrawRectangle(BorderPenControl, rect);
            }
        }

        private void OverrideControlBorder(Graphics g)
        {
            if (g == null) return;
            g.DrawRectangle(BorderPen, new Rectangle(0, 0, this.Width, this.Height - 1));
        }

        protected override void OnDropDown(EventArgs eventargs)
        {
            base.OnDropDown(eventargs);
            InvalidateSince = 0;
            DroppedDown = true;
            this.Invalidate();
        }

        protected override void OnCloseUp(EventArgs eventargs)
        {
            base.OnCloseUp(eventargs);
            DroppedDown = false;
            this.Invalidate();
        }

        protected override void OnLostFocus(System.EventArgs e)
        {
            base.OnLostFocus(e);
            this.Invalidate();
        }

        protected override void OnGotFocus(System.EventArgs e)
        {
            base.OnGotFocus(e);
            this.Invalidate();
        }
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.Invalidate();
        }

    }
}

////==========================OLD SOURCE========================================
//using System;
//using System.Collections.Generic;
//using System.Text;
//using System.Windows.Forms;
//using System.Drawing;

//namespace ELab.Controls.flatstyle
//{
//    public class FlatDateTimePicker: DateTimePicker
//    {
//        public FlatDateTimePicker()
//        {
//            borderDrawer.BorderColor = Color.FromArgb(181, 174, 132);
//        }

//        private BorderDrawer borderDrawer = new BorderDrawer();
//        protected override void WndProc(ref Message m)
//        {
//            base.WndProc(ref m);
//            borderDrawer.DrawBorder(ref m, this.Width, this.Height);
//        }

//        /// <summary>
//        /// Color of object's border
//        /// </summary>
//        public Color BorderColor
//        {
//            get { return borderDrawer.BorderColor; }
//            set
//            {
//                borderDrawer.BorderColor = value;
//                Invalidate();
//            }
//        }
//    }
//}